﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;

namespace RA_WebSite.Models
{
    public static class ElaboraAnalisi
    {
        // Variabile per comunicare con il database
        private static readonly RAContext db = new RAContext();

        /// <summary>
        /// Dato uno step (formato di più provini) eseguo l'analisi
        /// </summary>
        /// <param name="s"></param>
        public static Analisi EseguiAnalisiStep(Step s)
        {
            // Memorizzo i privini associati a uno step
            var provini = RecuperaProvini(s);

            // Se non ci sono provini associati allo steo...
            if (!provini.Any())
            {
                //...esco senza fare niente
                return null;
            }

            var analisiProvini = new List<Analisi>();
            // FACCIO LA MEDIA DELLE ANALISI
            int cont = 0;
            double mediaPercentuale = 0;

            char carattereTemp='Z';
            bool stessoTipo = false;

            foreach (var provino in provini)
            {
                // Cancello le vecchie analisi
                EliminaVecchieAnalisi(provino);
                //
                var risultatoAnalisi = RecuperaValoreDaPython(s.Foto,provino.Coordinate);
                // Seguo le varie operazioni per l'analisi
                var gradoPhyton = CalcolaPercentualeRuggineProvino(risultatoAnalisi);
                var tipoPhyton = CalcolaTipoRuggineProvino(risultatoAnalisi);
                //
                var gradoRa = -1;
                var tipoRa = -1;
   
                // per la media
                cont++;
                mediaPercentuale += gradoPhyton;

                var casi = new Dictionary<Func<double, bool>, Action>
                    {
                        {x => x < 0.1, () => gradoRa = 1},//new GradoDiRuggine {LimiteInferiore = -99999, LimiteSuperiore = 0.01},
                        {x => x > 0.01 && x <0.03, () => gradoRa = 2},//new GradoDiRuggine {LimiteInferiore = 0.01, LimiteSuperiore = 0.03},
                        {x => x > 0.03 && x <0.1, () => gradoRa = 3},//new GradoDiRuggine {LimiteInferiore = 0.03, LimiteSuperiore = 0.1},
                        {x => x > 0.1 && x <0.3, () => gradoRa = 4},//new GradoDiRuggine {LimiteInferiore = 0.1, LimiteSuperiore = 0.3},
                        {x => x > 0.3 && x <1.0, () => gradoRa = 5},//new GradoDiRuggine {LimiteInferiore = 0.3, LimiteSuperiore = 1.0},
                        {x => x > 1.0 && x <3.0, () => gradoRa = 6},//new GradoDiRuggine {LimiteInferiore = 1.0, LimiteSuperiore = 3.0},
                        {x => x > 3.0 && x <10.0, () => gradoRa = 7},//new GradoDiRuggine {LimiteInferiore = 3.0, LimiteSuperiore = 10.0},
                        {x => x > 10.0 && x <16.0, () => gradoRa = 8},//new GradoDiRuggine {LimiteInferiore = 10.0, LimiteSuperiore = 16.0},
                        {x => x > 16.0 && x <33.0, () => gradoRa = 9},//new GradoDiRuggine {LimiteInferiore = 16.0, LimiteSuperiore = 33.0},
                        {x => x > 33.0 && x <50.0, () => gradoRa = 10},//new GradoDiRuggine {LimiteInferiore = 33.0, LimiteSuperiore = 50.0},
                        {x => x > 50.0, () => gradoRa = 11},//new GradoDiRuggine {LimiteInferiore = 50.0, LimiteSuperiore = 99999}
                    };
                casi.First(kvp => kvp.Key(gradoPhyton)).Value();

                switch (tipoPhyton)
                {
                    case 'S':
                        tipoRa = 1;
                        break;
                    case 'G':
                        tipoRa = 2;
                        break;
                    case 'P':
                        tipoRa = 3;
                        break;
                }

                if (carattereTemp=='Z')
                {
                    carattereTemp = tipoPhyton;
                }
                else
                {
                    if (carattereTemp == tipoPhyton)
                    {
                        stessoTipo = true;
                    }
                }
                // Compongo l'oggetto che andrò a salvare sul db
                var analisi = new Analisi()
                    {
                        Provino_Id = provino.Id,
                        Provino = provino,
                        PercentualeDiRuggine = (int)gradoPhyton,
                        TipoDiRuggine_Id = tipoRa,
                        GradoDiRuggine_Id = gradoRa
                    };

                // Memorizzo l'analisi effettuata
                db.Analisis.Add(analisi);
                db.SaveChanges();

                analisiProvini.Add(analisi);
            }

            //ESEGUO L'ANALISI TOTALE

            mediaPercentuale = mediaPercentuale / cont;
            int gradoStep = -1;

            var casi2 = new Dictionary<Func<double, bool>, Action>
                    {
                        {x => x < 0.1, () => gradoStep = 1},//new GradoDiRuggine {LimiteInferiore = -99999, LimiteSuperiore = 0.01},
                        {x => x > 0.01 && x <0.03, () => gradoStep = 2},//new GradoDiRuggine {LimiteInferiore = 0.01, LimiteSuperiore = 0.03},
                        {x => x > 0.03 && x <0.1, () => gradoStep = 3},//new GradoDiRuggine {LimiteInferiore = 0.03, LimiteSuperiore = 0.1},
                        {x => x > 0.1 && x <0.3, () => gradoStep = 4},//new GradoDiRuggine {LimiteInferiore = 0.1, LimiteSuperiore = 0.3},
                        {x => x > 0.3 && x <1.0, () => gradoStep = 5},//new GradoDiRuggine {LimiteInferiore = 0.3, LimiteSuperiore = 1.0},
                        {x => x > 1.0 && x <3.0, () => gradoStep = 6},//new GradoDiRuggine {LimiteInferiore = 1.0, LimiteSuperiore = 3.0},
                        {x => x > 3.0 && x <10.0, () => gradoStep = 7},//new GradoDiRuggine {LimiteInferiore = 3.0, LimiteSuperiore = 10.0},
                        {x => x > 10.0 && x <16.0, () => gradoStep = 8},//new GradoDiRuggine {LimiteInferiore = 10.0, LimiteSuperiore = 16.0},
                        {x => x > 16.0 && x <33.0, () => gradoStep = 9},//new GradoDiRuggine {LimiteInferiore = 16.0, LimiteSuperiore = 33.0},
                        {x => x > 33.0 && x <50.0, () => gradoStep = 10},//new GradoDiRuggine {LimiteInferiore = 33.0, LimiteSuperiore = 50.0},
                        {x => x > 50.0, () => gradoStep = 11},//new GradoDiRuggine {LimiteInferiore = 50.0, LimiteSuperiore = 99999}
                    };
            casi2.First(kvp => kvp.Key(mediaPercentuale)).Value();

            var tipoTotale = 4;
            if (stessoTipo)
            {
                switch (carattereTemp)
                {
                    case 'S':
                        tipoTotale = 1;
                        break;
                    case 'G':
                        tipoTotale = 2;
                        break;
                    case 'P':
                        tipoTotale = 3;
                        break;
                }
            }


            return new Analisi()
                {
                    PercentualeDiRuggine = (int)mediaPercentuale,
                    GradoDiRuggine_Id = gradoStep,
                    TipoDiRuggine_Id = tipoTotale
                };

        }

        private static void EliminaVecchieAnalisi(Provino p)
        {
            var analisi = (from a in db.Analisis where a.Provino_Id == p.Id select a);
            foreach (var analisi1 in analisi)
            {
                db.Analisis.Remove(analisi1);
            }
           
            db.SaveChanges();
        }

        /// <summary>
        /// Metodo di supporto per recuperare tutti i provini associati a uno step dal database
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        private static List<Provino> RecuperaProvini(Step s)
        {
            // Recupera tutti i provini associati a uno step
            var provini = (from p in db.Provinoes where p.Step_Id == s.Id select p).ToList();
            return provini;
        }


        private static double CalcolaPercentualeRuggineProvino(string valorePython)
        {
            var parole = valorePython.Split(';');
            var valore = double.Parse(parole[0].Replace('.',','));
            return valore;
        }

        private static char CalcolaTipoRuggineProvino(string valorePython)
        {
            var parole = valorePython.Split(';');
            var car = Convert.ToChar(parole[1]);
            return car;

        }

        private static string RecuperaValoreDaPython(byte[] immagine, String coordinate)
        {
            var coo = coordinate.Split(' ');
            var ms = new MemoryStream(immagine);
            var returnImage = Image.FromStream(ms);
            var immagineTemp = Path.GetTempPath() + "\\immagineTemp.Jpeg";
            returnImage.Save(immagineTemp, ImageFormat.Jpeg);

            const string eseguibilePython = "C:/Python27/python.exe";
            string programmaAnalisi = AppDomain.CurrentDomain.GetData("DataDirectory") + "\\ScriptPython\\RA04.py";

            var p = new Process
            {
                StartInfo =
                {
                    FileName = eseguibilePython,
                    Arguments = String.Format(@"{5} --file {0} --x1 {1} --y1 {2} --x2 {3} --y2 {4}", immagineTemp, coo[0], coo[1], coo[2], coo[3], programmaAnalisi),
                    UseShellExecute = false,
                    RedirectStandardOutput = true
                }
            };
            p.Start();

            var output = p.StandardOutput.ReadToEnd().TrimEnd('\r', '\n');
            p.WaitForExit(100);

            return output;
        }


    }
}